package timer

import (
	"context"
	"sync"
	"time"

	"gitee.com/doraemon1996/bunny/goroutine"
	"gitee.com/doraemon1996/bunny/structure"
	"gitee.com/doraemon1996/bunny/uuid"
)

// 定时器结构体
type Timer struct {
	ctl *structure.Control

	// 定时任务表
	taskMap *sync.Map

	// 时间轮
	tw *timingWheel
}

// 定时任务tick
func (t *Timer) tick() {
	ticker := time.NewTicker(time.Millisecond)
	defer ticker.Stop()

	tasks := make([]*task, 0)
	lastTime := uint64(time.Now().UnixMilli())
	for !t.ctl.Closed() {
		now := <-ticker.C
		nowTime := uint64(now.UnixMilli())

		t.tw.Turn(lastTime, nowTime, &tasks)
		lastTime = nowTime

		for index, task := range tasks {
			tasks[index] = nil

			task.Submit()

			// 处理间隔时间执行的任务
			duration := task.GetInterval()
			if duration > 0 {
				t.tw.AddTask(duration, nowTime+duration, task)
			} else {
				t.taskMap.Delete(task.GetTaskID())
				releaseTask(task)
			}
		}

		tasks = tasks[len(tasks):]
	}
}

// 关闭定时器
func (t *Timer) Stop() {
	t.ctl.Stop(func() {
		t.ctl.Wait()

		t.tw.Clear()
		t.taskMap = nil
		t.tw = nil
	})
}

// 删除一个未执行任务
func (t *Timer) Remove(taskID uint64) {
	if t.ctl.Closed() {
		return
	}

	value, ok := t.taskMap.Load(taskID)
	if !ok {
		return
	}

	task := value.(*task)
	task.Cancel()
}

// 延时执行一次任务
func (t *Timer) Timeout(duration time.Duration, callback func()) uint64 {
	ms := duration.Milliseconds()
	if t.ctl.Closed() || ms < 1 {
		return 0
	}

	taskID := uuid.GenUint64()
	task := newTask(taskID, 0, callback)
	t.taskMap.Store(taskID, task)
	t.tw.AddTask(uint64(ms), uint64(time.Now().UnixMilli()+ms), task)

	return taskID
}

// 定时执行一次任务
func (t *Timer) Timing(timestamp int64, callback func()) uint64 {
	nowTime := time.Now().UnixMilli()
	if t.ctl.Closed() || timestamp <= nowTime {
		return 0
	}

	taskID := uuid.GenUint64()
	task := newTask(taskID, 0, callback)
	t.taskMap.Store(taskID, task)
	t.tw.AddTask(uint64(timestamp-nowTime), uint64(timestamp), task)

	return taskID
}

// 间隔时间执行任务
func (t *Timer) Interval(duration time.Duration, callback func()) uint64 {
	ms := duration.Milliseconds()
	if t.ctl.Closed() || ms < 1 {
		return 0
	}

	taskID := uuid.GenUint64()
	task := newTask(taskID, uint64(ms), callback)
	t.taskMap.Store(taskID, task)
	t.tw.AddTask(uint64(ms), uint64(time.Now().UnixMilli()+ms), task)

	return taskID
}

// 新创建一个定时器
func New(ctx context.Context) *Timer {
	timer := &Timer{
		ctl:     new(structure.Control),
		taskMap: new(sync.Map),
		tw:      newTimingWheel(),
	}

	timer.ctl.Init(ctx, nil)

	// 开始定时器循环
	timer.ctl.Start(func() {
		timer.ctl.Add(1)
		goroutine.Submit(func() {
			defer timer.ctl.Done()

			timer.tick()
		})
	})

	return timer
}
